﻿namespace Cavy.UdpClient
{
    public class MessageFactory
    {
        /// <summary>
        /// 已注册的帧数据缓存
        /// </summary>
        private readonly System.Collections.Generic.Dictionary<int, System.Type> _factory =
            new System.Collections.Generic.Dictionary<int, System.Type>()
            {
                {(int) MessageTypeEnum.FindRoomLan, typeof(Message_FindRoomLan)},
                {(int) MessageTypeEnum.FindRoomLanResponse, typeof(Message_FindRoomLanResponse)},
                {(int) MessageTypeEnum.JoinRoomRequest, typeof(Message_JoinRoomRequest)},
                {(int) MessageTypeEnum.JoinRoomResponse, typeof(Message_JoinRoomResponse)}
            };

        /// <summary>
        /// 自定义注册的帧数据
        /// </summary>
        private readonly System.Collections.Generic.Dictionary<int, System.Type> _customFactory =
            new System.Collections.Generic.Dictionary<int, System.Type>();
        
        /// <summary>
        /// 注册帧数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool Register<T>() where T : IMessage
        {
            var inst = System.Activator.CreateInstance<T>();
            return Register(inst);
        }
        
        /// <summary>
        /// 注册帧数据
        /// </summary>
        /// <param name="message"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public bool Register<T>(T message) where T : IMessage
        {
            var inst = message;
            var typeInt = inst.MessageType;
            if (_factory.ContainsKey(typeInt) || _customFactory.ContainsKey(typeInt))
            {
                UdpDebug.LogError($"Frame type {inst.GetType().Name}:{typeInt} already registered");
                return false;
            }
            _customFactory[typeInt] = typeof(T);
            return true;
        }

        /// <summary>
        /// 从 UdpRoom 或 UdpClient 注册帧数据
        /// </summary>
        /// <param name="messageFactory"></param>
        public void Register(MessageFactory messageFactory)
        {
            foreach (var e in messageFactory._customFactory)
            {
                _customFactory[e.Key] = e.Value;
            }
        }

        /// <summary>
        /// 检测是否已注册指定frameType的帧数据
        /// </summary>
        /// <param name="frameType"></param>
        /// <returns></returns>
        public bool HasRegister(int frameType)
        {
            return _factory.ContainsKey(frameType) || _customFactory.ContainsKey(frameType);
        }

        /// <summary>
        /// 读取帧数据
        /// </summary>
        /// <param name="gameId"></param>
        /// <param name="data"></param>
        /// <param name="endPoint"></param>
        /// <returns></returns>
        internal (bool success, IMessage message) _Analysis(uint gameId, byte[] data, IpEndPoint endPoint)
        {
            Serializable.NetDataReader reader = new Serializable.NetDataReader(data);
            try
            {
                var tempGameId = reader.GetUInt();
                if (tempGameId != gameId) return (false, null);
                var messageType = reader.GetInt();
                if (_factory.TryGetValue(messageType, out var type))
                {
                    IMessage frame = (IMessage) System.Activator.CreateInstance(type);
                    frame.Deserialize(reader);
                    frame.AnalysisData(this, endPoint);
                    return (true, frame);
                }
                return (true, null);
            }
            catch (System.Exception e)
            {
                UdpDebug.LogError(e);
            }
            return (false, null);
        }

        /// <summary>
        /// 读取帧数据
        /// </summary>
        /// <param name="gameId"></param>
        /// <param name="data"></param>
        /// <param name="endPoint"></param>
        /// <returns></returns>
        public IMessage Parse(uint gameId, byte[] data, IpEndPoint endPoint)
        {
            try
            {
                Serializable.NetDataReader reader = new Serializable.NetDataReader(data);
                var tempGameId = reader.GetUInt();
                if (tempGameId != gameId) return null;
                var messageType = reader.GetInt();
                if (_factory.TryGetValue(messageType, out var type) || _customFactory.TryGetValue(messageType, out type))
                {
                    IMessage message = (IMessage) System.Activator.CreateInstance(type);
                    message.Deserialize(reader);
                    message.AnalysisData(this, endPoint);
                    return message;
                }
                return null;
            }
            catch (System.Exception e)
            {
                UdpDebug.LogError(e);
            }
            return null;
        }

        public T As<T>() where T : MessageFactory
        {
            return (T)this;
        }
    }
}